package com.ufaud.ais.pub.webapp.action;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;

import com.ufaud.ais.pub.view.JQueryTreeViewNode;

/**
 * 功能：将业务类转化、输出成树形结构JSON的Action基类
 * @author 林可
 * @version 1.0 2010-7-20
 */
public abstract class JQueryTreeViewAction<T> extends BaseAction {

	private JQueryTreeViewNode root;
	private T actualObject;
	private Class<?> actualObjectClazz;
	private Map<String, String> urlPrefixMap = new HashMap<String, String>();

	public String execute() throws Exception {
		actualObject = loadActualObject();
		actualObjectClazz = actualObject.getClass();
		root = buildTreeNode(actualObject);
		return SUCCESS;
	}

	/*
	 * 功能: 将业务节点对应成树节点
	 * @author 林可  2010-7-20 上午12:23:21
	 * @param actualObject
	 * @return JQueryTreeViewNode
	 * @throws Exception
	 */
	private JQueryTreeViewNode buildTreeNode(T actualObject) throws Exception {
		JQueryTreeViewNode node = new JQueryTreeViewNode();

		String textFieldName = getTextFieldName();
		if (StringUtils.trimToNull(textFieldName) != null) {
			PropertyDescriptor textProperty = BeanUtils.getPropertyDescriptor(actualObjectClazz, textFieldName);
			Method textReadMethod = textProperty.getReadMethod();
			Object text = textReadMethod.invoke(actualObject, new Object[0]);
			if (text != null) {
				node.setText(text.toString());
			}
		}

		String idFieldName = getIdFieldName();
		if (StringUtils.trimToNull(idFieldName) != null) {
			PropertyDescriptor idProperty = BeanUtils.getPropertyDescriptor(actualObjectClazz, idFieldName);
			Method idReadMethod = idProperty.getReadMethod();
			Object id = idReadMethod.invoke(actualObject, new Object[0]);
			if (id != null) {
				node.setId(id.toString());
			}
		}

		// TODO 暂时都展开
		node.setExpanded(true);

		String childrenFieldName = getChildrenFieldName();
		if (StringUtils.trimToNull(childrenFieldName) != null) {
			PropertyDescriptor childrenProperty = BeanUtils.getPropertyDescriptor(actualObjectClazz, childrenFieldName);
			Method childrenReadMethod = childrenProperty.getReadMethod();
			Object children = childrenReadMethod.invoke(actualObject, new Object[0]);
			if (children != null && children instanceof Collection<?>) {
				Collection<T> childrenCollection = (Collection<T>) children;
				if (childrenCollection.size() != 0) {
					// Class<?> colClazz = childrenCollection.getClass();
					// Collection<JQueryTreeViewNode> childrenNodeCollection =
					// (Collection<JQueryTreeViewNode>) colClazz.newInstance();
					// TODO 用HashSet会导致子节点无序排列，待解决
					Set<JQueryTreeViewNode> childrenNodeCollection = new HashSet<JQueryTreeViewNode>();
					for (Iterator<T> iter = childrenCollection.iterator(); iter.hasNext();) {
						childrenNodeCollection.add(buildTreeNode(iter.next()));
					}
					node.setChildren(childrenNodeCollection);
					node.setHasChildren(true);
				} else {
					node.setHasChildren(false);
				}
			}
		}

		if (urlPrefixMap.size() > 0 && node.getId() != null) {
			processUrls(node);
		}

		return node;
	}

	/*
	 * 功能: 为节点生成Url
	 * @author 林可  2010-7-20 上午12:29:33
	 * @param node
	 */
	private void processUrls(JQueryTreeViewNode node) {
		Map<String, String> urls = new HashMap<String, String>();
		String context = getRequest().getContextPath();
		for (Iterator<String> iter = urlPrefixMap.keySet().iterator(); iter.hasNext();) {
			String key = iter.next();
			String url = urlPrefixMap.get(key);
			url = url.replaceAll("\\$\\{ctx\\}", context);
			url = url.replaceAll("\\$\\{id\\}", node.getId());
			url = url.replaceAll("\\$\\{text\\}", node.getText());
			urls.put(key, url);
		}
		node.setUrls(urls);
	}

	/**
	 * 功能: 子类实现此方法取得实际业务对象
	 * 
	 * @author 林可 2010-7-14 下午01:46:38
	 * @return
	 */
	protected abstract T loadActualObject();

	/**
	 * 功能: 指定将实际业务对象的哪个字段显示在树中
	 * 
	 * @author 林可 2010-7-14 下午01:47:01
	 * @return
	 */
	protected abstract String getTextFieldName();

	/**
	 * 功能: 指定实际业务对象的id字段，一般是在形成url时用
	 * 
	 * @author 林可 2010-7-14 下午01:47:44
	 * @return
	 */
	protected abstract String getIdFieldName();

	/**
	 * 功能: 指定实际业务对象的子节点集合字段
	 * 
	 * @author 林可 2010-7-14 下午01:48:23
	 * @return
	 */
	protected abstract String getChildrenFieldName();

	/**
	 * 功能: 指定该节点所需的url, 目前支持如下一些占位符：
	 * ${ctx} -- 上下文
	 * ${id} -- 节点的Id，子类有效地重写了getIdFieldName()方法时有效替换
	 * ${text} -- 节点的Text，，子类有效地重写了getTextFieldName()方法时有效替换
	 * 
	 * @author 林可  2010-7-15 上午12:17:57
	 * @param displayName
	 * @param urlString
	 * @see getIdFieldName()
	 * @see getTextFieldName()
	 */
	protected final void putUrl(String displayName, String urlString) {
		urlPrefixMap.put(displayName, urlString);
	}

	public final JQueryTreeViewNode getRoot() {
		return root;
	}

}